/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_CONTAINER_PAGES_HPP
#define	_CUBEFS_CORE_CONTAINER_PAGES_HPP

#include <cubefs/core/tools/types/typelist.hpp>
#include <cubefs/core/container/builder.hpp>

#include <cubefs/core/tools/bitmap.hpp>
#include <cubefs/core/tools/buffer.hpp>
#include <cubefs/core/tools/reflection.hpp>
#include <cubefs/core/tools/types/typehash.hpp>
#include <cubefs/api/api.hpp>
#include <cubefs/core/tools/reflection.hpp>

#define CUBEFS_SOURCE "cubefs/core/container/pages.cpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {


using cubefs::TypeList;
using namespace cubefs::tools;


template <typename T, size_t Size = sizeof(T)>
class AbstractPageID: public ValueBuffer<T, Size> {
public:
    typedef AbstractPageID<T, Size>                 ValueType;
    typedef ValueBuffer<T, Size>                 	Base;

    AbstractPageID(): Base() {}

    AbstractPageID(const T &t) : Base(t) {}

    AbstractPageID(const IDValue& id): Base() {
        Base::CopyFrom(id.ptr());
    }

    bool is_null() const {
        return IsClean(Base::ptr(), Size);
    }

    void set_null() {
        Clean(Base::ptr(), Size);
    }
};

template <typename T, size_t Size>
static Logger& operator<<(Logger &log, const AbstractPageID<T, Size>& value) {
    IDValue id(&value);
    log.log(id);
    return log;
}


template <Int Size>
class BitBuffer: public Buffer<Size % 8 == 0 ? Size / 8 : ((Size / 8) + 1)> {
    typedef Buffer<(Size % 8 == 0 ? Size / 8 : ((Size / 8) + 1))>               Base;
public:

    typedef Int                         Index;
    typedef Long                        Bits;

    static const Int kBitSize           = Size;

    static const int RESERVED_SIZE      = 0;
    static const int RESERVED_BITSIZE   = RESERVED_SIZE * 8;

    BitBuffer() : Base() {}

    bool is_bit(Index index) const {
        return GetBit(*this, index + RESERVED_BITSIZE);
    }

    Bits get_bits(Index idx, Index count) const {
        return GetBits(*this, idx, count);
    }

    void set_bits(Index idx, Bits bits, Index count) {
        SetBits(*this, idx, bits, count);
    }

    void set_bit(int index, int bit) {
        SetBit(*this, index + RESERVED_BITSIZE, bit);
    }
};

template <typename PageIdType, Int FlagsCount = 32>
class AbstractPage {

    typedef AbstractPage<PageIdType, FlagsCount> Me;

    typedef BitBuffer<FlagsCount> FlagsType;

    PageIdType  id_;
    FlagsType   flags_;
    Int         crc_;
    Int         model_hash_;
    Int         page_type_hash_;
    
public:
    typedef PageIdType      ID;

    AbstractPage(): flags_(), id_() {}

    AbstractPage(const PageIdType &id): flags_(), id_(id) {}

    const PageIdType &id() const {
        return id_;
    }

    PageIdType &id() {
        return id_;
    }

    const FlagsType &flags() const {
        return flags_;
    };

    void init() {}

    FlagsType &flags() {
        return flags_;
    };

    Int &crc() {
        return crc_;
    }

    const Int &crc() const {
        return crc_;
    }

    Int &model_hash() {
        return model_hash_;
    }

    const Int &model_hash() const {
        return model_hash_;
    }

    Int &page_type_hash() {
        return page_type_hash_;
    }

    const Int &page_type_hash() const {
        return page_type_hash_;
    }

    Int data_size() const {
        return sizeof(Me);
    }

    void *operator new(size_t size, void *page) {
        return page;
    }

    void operator delete(void *buf) {}

    //Rebuild page content such indexes using provided data.
    void Rebiuild(){}

    void BuildFieldsList(MetadataList &list, Long &abi_ptr) const {
        FieldFactory<PageIdType>::create(list, id(),        "ID",               abi_ptr);
        FieldFactory<FlagsType>::create(list,  flags(),     "FLAGS",            abi_ptr);
        FieldFactory<Int>::create(list,  crc(),             "CRC",              abi_ptr);
        FieldFactory<Int>::create(list,  model_hash(),      "MODEL_HASH",       abi_ptr);
        FieldFactory<Int>::create(list,  page_type_hash(),  "PAGE_TYPE_HASH",   abi_ptr);
    }

    template <typename PageType>
    void CopyFrom(PageType* page)
    {
        this->id()              = page->id();
        this->flags()           = page->flags();
        this->crc()             = page->crc();
        this->model_hash()      = page->model_hash();
        this->page_type_hash()  = page->page_type_hash();
    }
};


template <typename PageType, Int PageSize>
class PageWrapper: public cubefs::api::PageImpl {
    PageType *page_;
public:
    PageWrapper(PageType* page): page_(page) {}
    PageWrapper(): page_(NULL) {}

    virtual bool IsNull() const {
    	return page_ == NULL;
    }

    virtual IDValue GetId()
    {
    	if (page_ != NULL)
    	{
    		return IDValue(&page_->id());
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Page data is not set");
    	}
    }

    virtual Int GetModelHash()
    {
    	if (page_ != NULL)
    	{
    		return page_->model_hash();
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Page data is not set");
    	}
    }

    virtual Int GetPageTypeHash()
    {
    	if (page_ != NULL)
    	{
    		return page_->page_type_hash();
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Page data is not set");
    	}
    }
    
    virtual BigInt GetFlags() {
        return 0;
    }
    
    virtual void* Ptr() {
        return page_;
    }

    virtual void SetPtr(void* ptr)
    {
    	page_ = static_cast<PageType*>(ptr);
    }

    virtual Int Size() {
        return PageSize;
    }

    virtual Int GetByte(Int idx)
    {
    	if (page_ != NULL)
    	{
    		if (idx >= 0 && idx < PageSize) {
    			return ((UByte*)page_)[idx];
    		}
    		else {
    			throw BoundsException(CUBEFS_SOURCE, "Invalid byte offset", idx, 0, PageSize);
    		}

    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Page data is not set");
    	}
    }

    virtual void SetByte(Int idx, Int value)
    {
    	if (page_ != NULL)
    	{
    		if (idx >= 0 && idx < PageSize) {
    			((UByte*)page_)[idx] = (UByte)value;
    		}
    		else {
    			throw BoundsException(CUBEFS_SOURCE, "Invalid byte offset", idx, 0, PageSize);
    		}
    	}
    	else {
    		throw NullPointerException(CUBEFS_SOURCE, "Page data is not set");
    	}
    }
};


template <typename Name, typename Base>
class PagePart;

template <typename Name>
class PagePartNotFound;

//template <typename CurrentProfile, typename Name, typename Base>
//class PagePart<NullType, CurrentProfile, Name, Base> {
//public:
//    typedef PagePartNotFound<Name>                                              Locate;
//};

class EmptyPart{};

//template <typename CurrentProfile, typename Base>
//class PagePart<RootProfile, CurrentProfile, EmptyPart, Base>: public Base {
//public:
//    typedef PagePart<RootProfile, CurrentProfile, EmptyPart, Base>   Locate;
//};



template <
        typename Profile,
        typename PartsList,
        typename Base
>
struct PageBuilder: public Builder<PartsList, PagePart, Base> {};

}

namespace api {

template<typename Type, Int Size, size_t Size0>
struct TypeNameFactory<cubefs::ctr::AbstractPageID<Type, Size0>, Size>
{
    static String name() {
        return String("cubefs::ctr::AbstractPageID<"+TypeNameFactory<Type>::name()+">");
    }
};

}


}




#undef CUBEFS_SOURCE

#include <cubefs/core/container/page_traits.hpp>

#endif	// _CUBEFS_CORE_CONTAINER_PAGES_HPP
